import socket
import select
import queue
from core.server import EOM

#singal process  singal thread
#multi
#select poll epoll

class Server():
    def __init__(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # server socket
        self.socket.setblocking(0) # 我不要阻塞在应用程序 因为使用了多路复用
        self.sockets = [] # client socket list
        self.rList = [self.socket,] # listen read list,put server socket put to rlist
        self.wList = [] # listen write list
        self.messageQ = {} #dict type,put key-value.key:socket and value:queue[str]

    def start(self):
        self.socket.bind(("0.0.0.0",8888))
        self.socket.listen(20)
        while True:
            #select 不是python interpreter调用的方法。linux or window 内核提供的接口。我们这里使用的 C API。
            # 三个结果就是我们内核有了信息后，放入的数据。
            #如果select一直循环就一直浪费cpu，我们不希望这样，所以这个select是一个阻塞方法。
            #我们传入的参数是我们要监控的list
            readable, writeable, exceptions = select.select(self.rList,self.wList,self.rList)
            #read func
            for s in readable:
                if s is self.socket:
                    #这里有新连接介入,accpet方法不是阻塞的因为我们设置了setBlocking(false)
                    cs, addr = s.accept()
                    cs.setblocking(0) # non blocking
                    self.sockets.append(cs)
                    self.rList.append(cs) # put to rList for listen
                    self.messageQ[cs] = queue.Queue()#client socket连接server时候，需要建立一个queue来存放我们的信息，queue是线程安全的
                else:
                    #这里是client socket的信息发送
                    content = "" #接收的内容
                    while True:
                        try:
                            content += s.recv(1024).decode("utf-8")# decode from byte array to str,recv func is not blocking funcation .so we dont need create new thread.
                        except Exception as e:
                            print(e)
                            self.end(s)
                            break# client断开连接了
                        if content == "" or content.endswith(EOM):#content is null or end with EOM(\n)
                            break
                    if content == "":#空代表client断开连接了
                        continue
                    #TODO anything
                    #开始发送信息
                    print(content)
                    #循环所有已经连接到的socket
                    for conn in self.sockets:
                        #我们不要去直接send信息，应该去把要发的信息存档到self.messageQ
                        #我们还要把我的当前的socket 加入到wList进行监听
                        self.messageQ[conn].put(content)
                        self.wList.append(conn)# 下次机会调用下面的writeable的for循环
            #write func
            for s in writeable:
                content = ""
                try:
                    content = self.messageQ[s].get_nowait()
                except queue.Empty:
                    print("error")
                    self.end(s)
                else:
                    #获取到了信息 需要发送给每一个客户端
                    s.send(content.encode("utf-8"))
                    #把socket 从wlist remove ,以免下一次继续进入改for循环
                    self.wList.remove(s)
            #excpet func
            for s in exceptions:
                self.end(s)

    #当我们某个client socket断开连接的时候调用
    def end(self,s :socket.socket):
        print("client close")
        if s in self.rList:
            self.rList.remove(s)
        if s in self.wList:
            self.wList.remove(s)
        del self.messageQ[s]
        self.sockets.remove(s)

if __name__ == "__main__":
    Server().start()